12주차 - aws lattice, gateway api
개요
사전 지식
실습 진행
간단한 서버 통신
첫번째 라티스 활용 실습은 위와 같이 기본적인 eks 클러스터를 구축하고, 다른 vpc에서 접근하여 통신하는 것이다.[1]
이번에도 최대한 실습을 잘 따라가면서 내용을 정리하는 것을 주목적으로 삼는다.
내 커스텀 설정을 넣어서 실습을 하고 싶지만 그러기에 당장 시간이 조금 부족하다..
git clone https://github.com/aws-ia/terraform-aws-eks-blueprints.git
블루프린트 레포 파일을 받는다.
/patterns/vpc-lattice/client-server-communication/main.tf
에서 리전 정보만 바꿔주면 기본 준비는 끝이다.
terraform init
terraform apply -target="module.client_vpc" -auto-approve
terraform apply -target="module.cluster_vpc" -auto-approve
terraform apply -target=aws_route53_zone.primary -auto-approve
terraform apply -target="module.client_sg" -auto-approve
terraform apply -target="module.endpoint_sg" -auto-approve
terraform apply -target="module.client" -auto-approve
terraform apply -target="module.vpc_endpoints" -auto-approve
terraform apply -target="module.eks" -auto-approve
terraform apply -target="module.addons" -auto-approve
terraform apply -auto-approve
aws eks update-kubeconfig --name client-server-communication --alias client-server-communication --region ap-northeast-2
내가 가장 싫어하는 타겟 방식..
내 생각에는 테라폼에는 파이프라인 스크립트 기능이 필요하다.
각 리소스 간 종속성을 관리해주는 것은 좋지만 세팅을 하다보면 필연적으로 어떤 리소스가 적용돼야만 다른 리소스를 사용할 수 있다던가 하는 것들이 있다.
이럴 때 파일을 단계적으로 적용시켜서 순서를 명확히 할 수 있는 기능이 있다면 관리가 훨씬 편해질 것이다.
코드 분석
client.tf
client 파일에는 eks와 통신하기 위한 ec2와 vpc에 대한 세팅이 담겨 있다.
SSM으로 세션 연결을 할 수 있도록 정책이 포함돼있다.
vpc 엔드포인트가 뚫리는데, SSM을 사용하기 위한 세팅이며, 이를 위한 보안 그룹도 세팅된다.
eks.tf
다음으로 eks는 1.30버전으로 별도의 vpc에 프라이빗 서브넷 쪽에 만들어진다.
프라이빗 서브넷에 eks가 배치되니 기본적으로 인터넷으로 나가기 위한 nat 게이트웨이도 만든다.
다음으로는 게이트웨이api 컨트롤러와 external dns를 설치한다.
external dns 부분은 아래에서 다루겠다.
그 다음 데모 어플리케이션을 헬름을 통해 배포하는 것을 확인할 수 있다.
차트는 같은 디렉토리에서 찾아볼 수 있다.
apiVersion: gateway.networking.k8s.io/v1beta1
kind: GatewayClass
metadata:
name: amazon-vpc-lattice
spec:
controllerName: application-networking.k8s.aws/gateway-api-controller
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: my-services
namespace: apps
spec:
gatewayClassName: amazon-vpc-lattice
listeners:
- name: http
protocol: HTTP
port: 80
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: server
namespace: apps
spec:
hostnames:
- server.example.com
parentRefs:
- name: my-services
sectionName: http
rules:
- backendRefs:
- name: server
kind: Service
port: 8090
matches:
- path:
type: PathPrefix
value: /
해당 차트에는 기본적인 게이트웨이 api 리소스들에 대한 양식이 정의돼있다.
게이트웨이에 정의된 80번 포트는 httpRoute에서 받아 데모 서비스의 8090포트로 보내도록 돼있다.
마지막으로 클러스터 노드들의 보안 그룹에 lattice의 ip 대역을 받을 수 있도록 룰을 추가한다.
lattice.tf
lattice 파일에서는 라티스 서비스 네트워크를 만들고, 여기에 각 vpc를 연결한다.
데모 어플리케이션이 배포된 이후 2분을 강제로 기다리도록 세팅돼 있는데, 이걸 depends 하는 블록이 아무것도 없다.
그냥 프로비저닝 단계에서 다른 리소스들이 배포될 동안 시간을 버는 리소스라 생각하면 될 것 같다.
마지막으로 데모 어플리케이션으로 사용할 도메인에 대해 클라이언트 vpc에서 route53을 사용하도록 세팅한다.
eks 파일에서 external dns는 이 도메인을 위해서 존재한다.
즉, example.com은 external dns를 통해 route53에 등록되고 클라이언트 vpc는 해당 존의 영향을 받게 되므로 데모 주소를 현재 아키텍쳐 내에서 질의 받을 수 있게 되는 구조이다.
클라이언트에서 데모 어플리케이션 통신
배포된 환경을 분석하기 이전에 제대로 배포됐는지 확인해보자.
블루프린트 예제는 기본적으로 직접 세팅할 게 없다보니 실습 후, 사후 분석을 하는 방식으로 진행하게 되더라.
kubectl logs -f deployment/server -n apps --all-containers=true --since=1m
통신이 제대로 되는지 확인하기 위해 서버 쪽에서도 로그를 잠시 걸어둔다.
curl -i http://server.example.com
ssm으로 들어간 인스턴스에서는 그냥 요청을 날리기만 하면 된다.
응답이 제대로 파드로부터 오는 것이 확인된다.
서버 파드를 보아도 라티스에서 실행되는 헬스체킹들이 보이나 제대로 curl 요청을 받은 것을 확인할 수 있다.
2025/04/26 11:20:41 Receiving %!(EXTRA *http.Request=&{GET / HTTP/1.1 1 1 map[Accept:[*/*] User-Agent:[curl/8.3.0] X-Amzn-Lattice-Network:[SourceVpcArn=arn:aws:ec2:ap-northeast-2::vpc/vpc-037b672c4bad7e46c] X-Amzn-Lattice-Target:[ServiceArn=arn:aws:vpc-lattice:ap-northeast-2::service/svc-06159a634e1a2693d; ServiceNetworkArn=arn:aws:vpc-lattice:ap-northeast-2::servicenetwork/sn-0e258440c1c304c69; TargetGroupArn=arn:aws:vpc-lattice:ap-northeast-2::targetgroup/tg-07f926c2415770d89] X-Amzn-Source-Vpc:[vpc-037b672c4bad7e46c] X-Forwarded-For:[10.1.2.114]] {} <nil> 0 [] false server.example.com map[] map[] <nil> map[] 169.254.171.196:7250 / <nil> <nil> <nil> 0xc0001c8940})
해당 로그를 조금 더 자세히 보면 들어온 요청의 헤더값들을 확인할 수 있다.
일단 요청이 온 vpc의 arn 정보와 라티스 서비스 arn도 나온다.
해당 도메인으로의 질의는 라티스를 거친다는 것을 간략하게 짐작할 수 있다.
콘솔을 통한 분석
그럼 본격적으로 어떻게 환경이 구성됐길래 이게 가능한 건지도 알아보자.
먼저 vpc 콘솔에서 라티스에 서비스 네트워크를 들어가보면 하나의 서비스 네트워크가 생성된 것을 확인할 수 있다.
사진으로 찍진 않았으나 테라폼에서 설정했던 두 개의 vpc 연결도 확인할 수 있다.
이 서비스 네트워크에 등록된 하나의 서비스가 보이는데, 이것이 gateway api controller에 의해 만들어진 것으로 짐작할 수 있다.
해당 서비스 연결 id를 보면 도메인 이름이 두 개 있는 것을 볼 수 있다!
그 중 하나는 http route에서 설정한 도메인이다.
아예 해당 서비스에 들어가서 보면 라우팅 탭에 위에서 게이트웨이api 리소스로 설정했던 값들이 반영된 것이 보인다.
route에서 match
쪽 필드 그대로 리스너 룰로 만들어졌고, 어떤 서버로 포워딩을 하는 것을 확인할 수 있다.
구체적으로 해당 리소스는 타겟그룹으로, 이 타겟 그룹에는 데모 파드들이 위치한다.
위 파드들은 route에서 설정했던 서비스의 엔드포인트들이다.
하지만 이 정도로는 만족할 수 없다.
위의 설정들에 나온 도메인 이름이 구체적으로 ip 추적이 가능한 이유는 위에서 만들었던 route53 덕분이다.
보다시피 server.example.com
에 대해 cname 레코드가 만들어진 것을 볼 수 있다.
그리고 이에 대한 값은 서비스가 기본으로 가지던 이름이다.
이를 기반으로 다시 라티스 서비스를 들어가보면 조금 더 명확해진다.
이 서비스는 위 route53에 의해 질의가 될 수 있도록 세팅돼있는 상태이다.
클라이언트 vpc에서 도메인 질의를 했을 때 나왔던 169로 시작하는 ip는 aws에서 관리하는 ip 대역이다.
조금 더 구체적으로는 aws 관리형 접두사 리스트라고 해서, 서비스마다 부여하는 ip 대역이 있다.[2]
이 대역으로 가는 트래픽은 aws 측으로 넘어가는데 이 부분부터는 aws에서 알아서 트래픽이 우리가 설정한 리소스나 엔드포인트로 갈 수 있도록 도와준다.
마치 인스턴스에서 IMDS를 얻어오는 것과 같은 원리라고 보면 되겠다.
gateway api controller 동작 추적
kubectl logs deployment/aws-gateway-api-controller-aws-gateway-controller-chart -n aws-application-networking-system --all-containers=true > lattice.log
도대체 gateway api controller는 무슨 일을 했던 걸까?
로그를 뜯어본다.
가장 초반에는 실행이 시작되며 게이트웨이api 리소스에 와치를 걸고, 이후에 해당 리소스들이 생성되자 콘솔에서 봤던 것들이 정직하게 만들어지는 것을 확인할 수 있다.
이러한 동작을 할 수 있는 이유는 위와 같이 라티스에 대한 조작, 그리고 필요한 정보를 조회하기 위한 읽기 권한을 가지고 있기 때문이다.
리소스 정리
terraform destroy -target="module.client_vpc" -auto-approve
terraform destroy -target="module.cluster_vpc" -auto-approve
terraform destroy -target=aws_route53_zone.primary -auto-approve
terraform destroy -target="module.client_sg" -auto-approve
terraform destroy -target="module.endpoint_sg" -auto-approve
terraform destroy -target="module.client" -auto-approve
terraform destroy -target="module.vpc_endpoints" -auto-approve
terraform destroy -target="module.eks" -auto-approve
terraform destroy -target="module.addons" -auto-approve
terraform destroy -auto-approve
실습에 사용했던 것들을 일일히 지워준다.
역시나 문제가 발생한다.
gatewayapi 컨트롤러를 통해 만들어진 서비스가 삭제되지 않아서 기본 라티스를 지우는데 실패했고, 해당 서비스는 심지어 route53에 레코드를 등록하고 있기 때문에 발생하는 문제로 보인다.
공식 문서에서도 리소스를 정리할 때 직접적으로 먼저 배포된 리소스들을 정리하라고 나온다.[3]
이를 위해서는 서비스를 지워야 하는데, 그러려면 또 서비스 네트워크와의 연결을 먼저 지워야 한다;;
지금이야 전체 아키텍쳐를 한번에 지우려고 하니까 발생하는 이슈라고 칠 수 있는데, gateway api 리소스를 지울 때도 이런 문제가 생기진 않겠지?
route53도 직접 지웠고, 이후에 다시 테라폼 디스트로이를 시켰다.
호호 또 문제가 발생한다..
확인해봤을 때는 vpc, eks 전부 잘 삭제됐기 때문에 추가적으로 iam에 들어가서 라티스를 위한 롤만 직접 삭제해주었다.
멀티 클러스터 보안 통신 구축
이제 생소한 라티스와 게이트웨이api 맛보기는 끝났다.
조금 더 복잡한 아키텍처를 구성하고, 클러스터 내부에서 어떻게 설정이 이뤄졌는지도 조금 더 상세하게 보도록 한다.
이번에는 이렇게 아키텍처를 구성한다![4]
보기만 해도 어지러운데, 차근차근 뜯어보자.
먼저 전체 구성의 목표는 멀티 클러스터 간 보안 통신을 하는 것이다.[5]
각 클러스터는 다른 VPC에 위치하고 있는 상태에서 암호 통신을 할 것이다.
이때 흔한 서비스 메시가 그렇듯이, 각 클러스터의 어플리케이션은 통신 간 암호화와 보안에 대해 신경 쓸 필요 없게 세팅한다.
그래서 어플리케이션 코드의 수정을 하지 않을 수 있도록 각 어플리케이션 앞단에 사이드카 프록시로 엔보이를 둘 것이다.
이 엔보이를 배치하기 위해, 각 워크로드가 배치될 때 Mutating Admission Webhook을 해주는 Kyverno를 둔다.
키베르노는 배치되는 워크로드에 두 가지 작업을 한다.
- 초기화 컨테이너로 iptables를 조작해 각 어플리케이션의 요청이 엔보이의 리스너로 통하도록 만든다.
- 엔보이를 사이드카로 배치한다.
- 쿠버 사이드카 컨테이너는 아니고 그냥 앱 컨테이너로서 등록한다.
그리고 이렇게 배치된 엔보이가 해주는 역할은 구체적으로 다음과 같다.
- HTTPS 통신
- 이때 사용할 인증서는 aws의 PCA(Private Certificate Authority)로부터 서명을 받은 인증서를 ACM(AWS Certificate Manager)에 둔다.
- Sigv4 서명(aws에서 제작한 aws 리소스에 접근할 때 사용하는 서명 방식)
- 이 서명을 기반으로 라티스에 접근할 신원을 구성한다.
- 라티스에 접근할 권한은 파드 아이덴티티와 aws gateway api controller의 IAMAuthPolicy 리소스를 사용한다.
- 이를 통해 클러스터의 트래픽이 AWS IAM의 관리를 받게 되어 보안이 한층 더 강화된다.
그럼 위 아키텍쳐에 나온 번호에 맞춰 각 리소스와 동작을 정리하자면,
- HttpRoute 설정
- 라티스 gateway api로 서비스를 노출한다.
- 백엔드 서비스와 경로, 라우팅 규칙을 명시한다.
- 키베르노 정책을 설정한다.
- 엔보이 사이드카 주입
- aws 신원을 통해 aws api로의 요청을 서명한다.Automatically signs AWS API requests with AWS credentials
- 서비스 간 통신을 암호화한다.
- PCA
- 인증서를 관리한다.
- 구체적으로 라티스의 커스텀 도메인에 대한 인증서를 설정한다.
- 이를 기반으로 TLS 통신이 가능하게 한다.
- IAM 정책
- External DNS
- gateway api 컨트롤러의 DNS엔드포인트를 모니터링한다.
- 이에 대한 DNS 레코드를 Route53에 만들고 업데이트한다.
- App1 → App2 요청 흐름
- 서비스 네트워크를 통해 라우팅된다.
- IAM 정책을 통해 인증된다.
- 위에서 설정된 인증서를 통해 암호화 통신을 한다.
- App2 → App1 응답 흐름
- 사실 그냥 위와 같다.
여기에서 확인할 포인트는 이 정도로 정리했다.
- 엔보이의 동작
환경 세팅
기본 환경 세팅
이번 실습도 몇 가지 단계를 거쳐서 세팅을 해야 하는데, 먼저 두 클러스터가 사용하게 될 기본 인프라를 구성해야 한다.
https를 위한 기본적인 인증서 세팅, 도메인, 라티스 세팅을 진행해야 한다.
environment 디렉토리에 main.tf에서 리전을 수정해준다.
cd terraform-aws-eks-blueprints/patterns/vpc-lattice/cross-cluster-pod-communication/environment/
terraform init
terraform apply --auto-approve
일단 기본 환경을 배포한다.
여기에는 기본적인 route53과, 라티스에 통신을 하기 위해 사용되는 기본적인 롤이 들어있다.
이름을 보면 알겠지만, 이 롤은 나중에 엔보이를 위해 쓰인다.
각 클러스터 세팅
다음으로는 각 클러스터를 구축해야 한다.
cluster 디렉토리에서도 마찬가지로 main.tf에서 리전을 수정해주자.
보다시피 클러스터의 ip 대역을 동일하게 세팅한다.
대역 충돌이 발생하지 않는 라티스의 장점을 간접적으로 체험해볼 수 있으렷다.
cd ../cluster/
./deploy.sh cluster1
eval `terraform output -raw configure_kubectl`
./deploy.sh cluster2
eval `terraform output -raw configure_kubectl`
다음 각 클러스터를 배포한다.
해당 스크립트는 테라폼 워크스페이스를 분리해서 각각을 적용하도록 돼있다.
클러스터 코드 분석
테라폼 코드로 보면 전체적으로 이전 실습과 비슷하다.
그러나 주의 깊게 볼 부분이 있는데, 이번에는 컨트롤러를 설치할 때 사용할 서비스 네트워크를 명시한다.
두 클러스터가 배포될 때 둘다 lattice-gateway를 사용하게 되어 결과적으로 같은 서비스 네트워크를 사용한다.
다만 뮤테이팅을 걸기 위해 키베르노를 설치한다.
이번에는 단계를 나누어 플랫폼 차트를 먼저 배포하는 것을 볼 수 있다.
데모 어플리케이션이 배포되기 이전에 설정돼야 하는 키베르노 정책 리소스와 게이트웨이 리소스를 먼저 배포하는 것이다.
여기에 allowedCluster 부분이 설정돼있는데, 코드를 뜯어본 바로는 이 부분은 사실 없어도 된다.
(구체적으로는 플랫폼 차트 values 파일에 해당 필드가 없어도 된다.)
게이트웨이 리소스에 조금 변화가 있는데, 이번에는 네임스페이스를 지정하면서 셀렉터를 기반으로 매칭한다.
tls 세팅에 대해서는 두 가지 진입점을 만드는데, 이것도 실습 세팅 상에서는 사실 custom domain쪽만 있으면 된다.
여기에 aws gateway api controller만의 리소스인 IAMAuthPolicy도 볼 수 있다.
게이트웨이에 라티스의 서비스를 호출할 수 있는 권한을 정의한다.
여기에 키베르노의 리소스도 정의돼있다.
보다시피 초기화 컨테이너로 iptables를 건드려서 트래픽이 엔보이를 향하도록 두고 있고, 동시에 엔보이도 배포한다.
iptables 명령어는 다음의 설정을 담고 있다.
- EGRESS_PROXY라는 체인을 만든다.
- OUTPUT 체인에 특정 대역의 패킷을 EGRESS_PROXY 체인을 거치도록 한다.
- EGRESS_PROXY에서 root 사용자의 패킷은 그냥 리턴한다(이 체인에서 벗어나므로 다시 OUTPUT으로 간다).
- 그 다음 규칙으로 나머지 패킷은 전부 8080으로 리다이렉트시킨다.
- 8080은 키베르노 세팅을 보면 알 수 있듯이 엔보이의 포트이다.
즉, 나가는 모든 요청은 엔보이를 거치게 된다는 것이다.
근데 여기에서 조금 주의해서 봐야할 부분이 있다고 생각이 든다.
내가 iptables를 제대로 보고 있는 게 맞다면, 이건 들어오는 트래픽에 대해서는 작동하지 않는다.
아까 위에서 어플리케이션은 http로 통신을 한다고 했는데, 요청을 보내는 측에서 http 요청을 보내는 것이 https로 변환되는 것만 제대로 보장되는 세팅인 것이다.
하지만 위에서 httproute에 tls 관련 라우팅 시 터미네이션이 일어나도록 세팅됐기 때문에 결과적으로는 요청을 받는 측도 http 요청을 받게 될 것이다.
그럼 엔보이는 어떤 식으로 세팅이 되는가?
해당 설정은 여기에서 확인할 수 있다.[6]
일단 이건 엔보이 설정 파일이다.
http 필터에 흥미로운 게 두 개 보인다.
일단 아래에는 SigV4 서명 설정 필터가 여기에 있는데, 특정 헤더를 제외한 트래픽에 대해 서명을 진행하는 설정이 담겨있다.
그리고 그 위로 포워드 프록시로서의 필터가 들어가는데, dns 질의를 127.0.0.1로 거는 것이 보인다..
사실 이 부분은 명확하게 아직 파악을 못 했다.
다만 일반적인 엔보이 세팅에 기반해 추측을 하자면, 단서는 클러스터 쪽 설정에 있다.
클러스터에는 엔드포인트가 있어야 하는데, 여기에는 엔드포인트가 보이지 않는다.
대신 클러스터 타입에 대한 정의가 내려져 있는데, 여기에도 동적 포워드 프록시로 세팅이 들어간다.
즉 현재 엔보이가 포워드 프록시로서 동작하고 있으며 실제 트래픽을 보내야 하는 업스트림 호스트를 dns 질의를 통해 찾아 보내도록 하는 세팅이라는 것이다.
클러스터 쪽에 tls 설정이 들어간 것이 보인다.
cat /etc/envoy/envoy.yaml.in | envsubst \$AWS_REGION,\$JWT_AUDIENCE,\$JWT_JWKS,\$JWT_ISSUER,\$JWKS_HOST,\$APP_DOMAIN > /etc/envoy/envoy.yaml
aws acm-pca get-certificate-authority-certificate --certificate-authority-arn $CA_ARN --region $AWS_REGION --output text > /etc/pki/ca-trust/source/anchors/internal.pem
update-ca-trust extract
cat /etc/envoy/envoy.yaml
/usr/local/bin/envoy --base-id 1 -l trace -c /etc/envoy/envoy.yaml
실제 엔보이가 실행될 때는 이 스크립트를 거친다.
위에 환경변수로서 세팅된 값들을 먼저 갈아치우고, PCA로부터 인증서를 받아온다.
(이게 아까 기본 환경 세팅 부분에서 봤던 롤이 사용되는 부분이다.)
그리고 컨테이너 속 신뢰된 CA 정보를 업데이트한다.
결과적으로 앞으로 통신할 때 사용되는 인증서를 서명한 CA에 대해 엔보이는 정말 신뢰할 수 있는 신뢰 기관으로서 인식하게 될 것이다.
위 플랫폼 차트가 배포된 이후에 데모 어플리케이션 차트가 배포된다.
디플로이먼트에서는 실행 프로세스의 그룹이 1000이 되도록 세팅한다.
결과적으로 데포 어플리케이션의 트래픽은 위 iptables 규칙 상에서 8080포트로 보내질 것이다.
httproute는 여기에 있는데, 위에서 본 두 개의 tls 게이트웨이 중 커스텀 도메인 부분만 활용하는 것을 확인할 수 있다.
이번에는 httproute에 대한 IAMAuthPolicy 리소스도 만든다.
여기에서 allowed 관련 필드가 사용되는데, 이 httpRoute로 들어올 수 있는 트래픽의 명세를 정의하는 것으로 보인다.
이 설정은 라티스 서비스로 들어가는 트래픽에 대해 위 조건을 만족시키지 않는 요청을 거부시킬 것이다.
확인
kubectl --context eks-cluster1 \
exec -ti -n apps deployments/demo-cluster1-v1 -c demo-cluster1-v1 \
-- curl demo-cluster2.example.com
각 클러스터에서 서로를 향해 요청을 날렸을 때 응답이 돌아온다면 성공이다.
kubectl --context eks-cluster1 exec -ti -n apps deployments/demo-cluster1-v1 -c demo-cluster1-v1 -- curl demo-cluster1.example.com
그러나 보다시피 자신을 향한 요청은 성공하지 못한다.
이건 위에서 IAMAuthPolicy에 서로의 클러스터에 대해서만 통신을 허용했기 때문이다.
kubectl --context eks-cluster1 exec -ti -n apps deployments/demo-cluster1-v1 -c demo-cluster1-v1 -- curl demo-cluster1-v1
물론 위 통신은 라티스를 통하는 도메인을 이용했기 때문에 막히는 거고, 그냥 클러스터 내부의 서비스 도메인을 이용해 요청하면 당연히 통신이 가능하다.
apiVersion: v1
kind: Pod
metadata:
name: debug
namespace: apps
spec:
containers:
- image: nicolaka/netshoot
name: debug
command: [sh,-c,"tail -f /dev/null"]
terminationGracePeriodSeconds: 1
간단하게 테스트를 할 수 있는 파드를 배포해보자.
keti -n apps debug -- curl demo-cluster2.example.com
이 요청 역시 실패하는데, 이 파드는 위 엔보이가 세팅돼있지 않기에 라티스로 통신할 권한이 없기 때문이다.
하지만 언뜻 생각하면 이상하게 느껴지기도 한다.
일단 IAMAuthPolicy 상에서는 클러스터와 네임스페이스만 따지기 때문에 위 파드를 제한을 받을 일이 없다.
또한 eks 클러스터 콘솔에서 파드 아이덴티티 설정에 들어가보면, apps 네임스페이스의 default 서비스어카운트를 가지는 파드는 이 파드 아이덴티티를 가지고 aws 리소스와 통신을 할 수 있다.
이때 파드 아이덴티티는 이 조건을 만족하는 파드들이 해당 롤의 권한을 가진다는 의미 정도밖에 되지 않는다.
서비스 네트워크 액세스 정책을 보면 anonymous 주체는 통신을 할 수 없도록 돼있다.
방금 만든 디버그용 파드는 SigV4 서명이 돼있지 않기에 권한은 있을지라도 정확하게 신원이 누구인지 확인이 되지 않는다.
그렇기 때문에 익명으로서 요청이 인식되고 라티스의 서비스를 호출할 권한이 없다고 인식되어버리는 것이다.
하지만 데모 어플리케이션의 통신 주체는 엔보이이다.
엔보이는 위에서 보았듯이 http 요청에 대해 SigV4 서명을 받는 필터를 거친 후에 프록시 작업을 수행한다.
그렇기 때문에 엔보이의 신원은 명확하게 인식되고, 이로부터 통신이 가능해지게 되는 것이다.
그럼 어떻게 해야 내가 만든 커스텀 워크로드가 통신을 할 수 있을까?
apiVersion: apps/v1
kind: Deployment
metadata:
name: debug
namespace: apps
spec:
selector:
matchLabels:
app: debug
replicas: 1
template:
metadata:
labels:
app: debug
spec:
containers:
- image: nicolaka/netshoot
name: debug
command: [sh,-c,"tail -f /dev/null"]
terminationGracePeriodSeconds: 1
키베르노의 정책으로 디플로이먼트면 뮤테이팅이 발동하게 했으니 이렇게만 세팅하면 당연히 주입이 일어날 거라 생각했는데, 왜인지 주입이 일어나지 않았다.
리소스 정리
./destroy.sh cluster2
./destroy.sh cluster1
SN=$(aws vpc-lattice list-service-networks --query 'items[?name==`lattice-gateway`].id' --output text)
if [ -n "$SN" ]; then
aws vpc-lattice delete-service-network --service-network-id "$SN"
fi
cd ../environment
terraform destroy -auto-approve
결론
스터디 내용만 보고 실습을 먼저 진행해서 감을 잡고 작동 원리와 문서를 정리하려고 했는데, 상당히 난항을 겪었다.
게이트웨이 api를 이스티오에 적용하는 것을 먼저 해보다보니 자연스럽게 이스티오 기준으로 생각을 하면서 진행했던 것이 화근이었던 것 같다.
그걸 떠나서도 이번 실습이 AWS에 빠삭하지 않는 내게 꽤나 어려웠다는 것도 한 몫한 것 같다.
이번에 얻었던 수확 중 하나는 라티스에 대한 기본적인 사용법, 그리고 AWS sdk를 거치지 않는 요청은 파드 아이덴티티를 통하더라도 SigV4 서명이 되지 않는 다는 것.
이번 내용은 조금 더 개인 정리가 필요할 것 같다.
번외 - gateway 리소스로 새로운 서비스 네트워크 만들기?
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: test
namespace: lattice-gateway
spec:
gatewayClassName: amazon-vpc-lattice
listeners:
- name: http-listener
port: 81
protocol: HTTP
allowedRoutes:
kinds:
- kind: HTTPRoute
namespaces:
from: Selector
selector:
matchLabels:
allow-attachment-to-infra-gw: "true"
게이트웨이는 서비스 네트워크를 나타낸다고 했으니, 임의로 하나를 만들면 새로운 서비스 네트워크가 만들어질 것이라고 생각했다.
그러나 막상 해보니 서비스 네트워크를 찾을 수 없다면서 게이트웨이는 계속 준비되지 않았다.
여태까지 보니까 게이트웨이의 이름은 곧 서비스 네트워크의 이름이다.
그래서 서비스 네트워크를 만들어봤다.
그러자 성공적으로 게이트웨이가 초기화됐다.
여기에서 알 수 있었던 것은, AWS에서는 게이트웨이를 만든다고 해서 실제 리소스가 생성되는 것이 아니란 것이었다.
이것은 이스티오에서 게이트웨이 api를 사용할 때와 매우 다른 동작이다.
AWS에서는 gateway api로 라티스를 전체적으로 관리할 수 있도록 설계하고 만든 게 아닌 것으로 보인다.
오히려 이미 존재하는 라티스의 서비스 네트워크를 기반으로 그 위에 서비스를 설정할 수 있도록 만든 것이다.
게이트웨이 리소스가 활성화되도 vpc 결합조차 일어나지 않는다.
혹시 vpc 연결을 하는 리소스가 있지 않을까 해서 찾아보니 있긴 있었다.
어쩌면 서비스 네트워크가 알아서 만들어지지 않는다는 점에서 내가 너무 비약적으로 추측을 하고 있는 것일 수도 있겠다.
aws gateway api controller에 대한 생각 정리
아직 완전히 정리되지는 않았지만, 중간 중간 들었던 생각을 잠시 나열한다.
생소해서 그런 것도 있겠지만 라티스를 기반으로 하는 gateway api에는 조금 이질감이 들었다.
- 남북 트래픽
- 인그레스를 대체하는 목적이 가장 큰 gateway api인데, 남북 트래픽에 대한 기능이 없다.
- 동서 트래픽
- gateway api에서 동서 트래픽의 경우은 서비스를 parentRef로 두는 방식의 GAMMA 이니셔티브 프로젝트가 진행되고 있다.
- 이 동서 트래픽은 클러스터 내부의 동서 트래픽을 의미하는데, 라티스의 경우 서비스 네트워크라는, 클러스터 외부를 포괄하는 AWS 아키텍처 상의 트래픽을 동서 트래픽을 잡는다.
- 관리 주체가 불명확한 리소스
- 위 번외 실험을 하면서 느꼈던 부분이다.
- 보통 쿠버네티스를 위한 애드온들은 적절한 권한 세팅만 하면 클러스터 내부에서 aws 리소스를 관리할 수 있도록 하는 식으로 운영된다.
- 리소스의 관리 주체가 클러스터 내부의 컨트롤러로 명확하다고 볼 수 있다.
- 여기에서 조금 더 커스텀 운용을 할 때 이미 존재하는 aws 리소스를 클러스터 내부 커스텀 리소스로 참조하는 게 가능한 정도이다.
- 예를 들면 alb controller의 타겟그룹바인딩 리소스를 이미 존재하는 aws의 타겟그룹바인딩의 aws id를 이용해 매핑하는 식.
- 라티스를 활용할 때는 오히려 aws 쪽에 먼저 세팅을 하고 운용해야 하는 것 같다.
- 즉, 라티스를 이용하기 위해 클러스터 내부의 주체와 별개인 관리자의 조작이 추가적으로 들어가는 게 당연한 방식이라는 것.
- 게이트웨이와 서비스 네트워크의 결합도
- 이 둘은 서로 이름이 일치해야 한다.
- 게이트웨이는 네임스페이스 종속 리소스라 클러스터에 하나만 있는 것도 아니다.
- 근데 애초에 서비스 네트워크 기준으로 둘의 관계를 1:N 관계라고 이해하면 그렇게 이상하진 않은 것 같기도.
gateway api는 다양한 설정과 기능을 담을 수 있도록 계속 개발이 진행되고 있는 만큼, 지금 섣불리 판단하기는 이르다고 본다.
무엇보다 아직 내가 aws gateway api controller에 대해 함부로 판단을 할 수 있을 만큼의 지식이 없다.
관련 문서
이름 | noteType | created |
---|
참고
https://aws-ia.github.io/terraform-aws-eks-blueprints/patterns/network/client-server-communication/ ↩︎
https://docs.aws.amazon.com/vpc/latest/userguide/working-with-aws-managed-prefix-lists.html ↩︎
https://www.gateway-api-controller.eks.aws.dev/latest/guides/getstarted/#cleanup ↩︎
https://aws-ia.github.io/terraform-aws-eks-blueprints/patterns/network/cross-cluster-pod-communication/ ↩︎
https://aws.amazon.com/ko/blogs/containers/secure-cross-cluster-communication-in-eks-with-vpc-lattice-and-pod-identity-iam-session-tags/ ↩︎
https://github.com/aws-samples/amazon-eks-security-immersion-day/blob/mainline/docker/envoy/envoy.yaml.in ↩︎